#pragma once

#include <vector>
#include <functional>
#include <MMSystem.h>

namespace gtl
{

class timer
{
public:
	timer() 
	{
		m_event_id = 0;
		m_timer_id = 0;
		timeBeginPeriod(1);
	}

	~timer()
	{
		timeEndPeriod(1);
	}

public:
	template<class T, class MemFun>
	void start(int elapse, MemFun mem_fun, T* host, int event_id = 0)
	{
		namespace holder = std::placeholders;
		start(elapse, std::bind(std::mem_fn(mem_fun), host, holder::_1), event_id);
	}

	template<class Fun>
	void start(int elapse, Fun fun, int event_id = 0)
	{
		m_fun = fun;
		m_event_id = event_id;
		m_timer_id = timeSetEvent(0, elapse, TimerCallback, (ulong)this, TIME_PERIODIC);
	}

	void stop(int /*event_id*/ = 0)
	{
		timeKillEvent(m_timer_id);
	}

protected:
	static void __stdcall TimerCallback(uint uTimerID, uint uMsg, ulong dwUser, ulong dw1, ulong dw2)
	{
		timer* pThis = (timer*)dwUser;
		if(pThis == NULL)
			return;

		pThis->m_fun(pThis->m_event_id);
	}

private:
	timer(const timer&);
	void operator=(const timer&);

protected:
	int m_event_id;
	uint m_timer_id;
	std::function<void (int)> m_fun;
};

//namespace detail
//{
//	template<class Timer>
//	struct timer_base
//	{
//		struct timer_info 
//		{
//			int event_id;
//			int real_event_id;
//			UINT_PTR timer_id;
//			gtl::fun<void(int /*event_id*/)> timer_fun;
//			Timer* pthis;
//			int	instance_count;
//			bool pause;
//		};
//
//		typedef typename std::vector<timer_info>::iterator Iterator;
//
//		int m_instance_index;
//		static int s_instance_count;
//		static std::vector<timer_info> s_timer_info;
//	};
//
//	template<class T>
//	int timer_base<T>::s_instance_count = 0;
//	
//	template<class T>
//	std::vector<typename timer_base<T>::timer_info> timer_base<T>::s_timer_info;
//} // end of namespace detail
//
//class timer_sleep
//{
//public:
//	timer_sleep()
//	{
//		m_paused = false;
//	}
//
//public:
//	template<class T, class MemFun>
//	void start(int /*id*/, int millisecond, MemFun mem_fun, T* host)
//	{
//		if(mem_fun == NULL || host == NULL)
//			return;
//
//		m_millisecond = millisecond;
//		m_fun_timer = gtl::bind(gtl::mem_fn(mem_fun), host);
//
//		m_thread_timer.start(&timer_sleep::timer_proc, this);
//	}
//
//	template<class Fun>
//	void start(int /*id*/, int millisecond, Fun fun)
//	{
//		m_millisecond = millisecond;
//		m_fun_timer = fun;
//
//		m_thread_timer.start(&timer_sleep::timer_proc, this);
//	}
//
//	void stop(int)
//	{
//		m_thread_timer.stop();
//	}
//
//	void pause(int)
//	{
//		m_paused = true;
//	}
//
//	void resume(int)
//	{
//		m_paused = false;
//	}
//
//private:
//	unsigned long timer_proc()
//	{
//		//timeval tv = { m_millisecond / 1000, m_millisecond % 1000 };
//
//		while(m_thread_timer.running())
//		{
//			Sleep(m_millisecond);
//			if(!m_paused)
//				m_fun_timer(0);
//		}
//
//		m_thread_timer.ready_stop();
//
//		return 0;
//	}
//
//private:
//	timer_sleep(const timer_sleep&);
//	void operator=(const timer_sleep&);
//
//private:
//	int  m_millisecond;
//	bool m_paused;
//	gtl::thread<> m_thread_timer;
//	gtl::fun<void(int)> m_fun_timer;
//};
//
//class timer : public detail::timer_base<timer>
//{
//public:
//	timer() 
//	{
//		m_instance_index = ++s_instance_count;
//	}
//
//	~timer()
//	{
//		for(int i = (int)s_timer_info.size() - 1; i >= 0 ; --i) 
//		{
//			if(s_timer_info[i].pthis == this)
//			{
//				::KillTimer(NULL, s_timer_info[i].timer_id);
//				s_timer_info.erase(s_timer_info.begin() + i);
//			}
//		}
//	}
//
//public:
//	template<class T, class MemFun>
//	void start(int event_id, int elapse, MemFun mem_fun, T* host)
//	{
//		start(event_id, elapse, gtl::bind(gtl::mem_fn(mem_fun), host));
//	}
//
//	template<class Fun>
//	void start(int event_id, int elapse, Fun fun)
//	{
//		int event_id_tmp = (m_instance_index << 24) + event_id;
//		for(size_t i = 0; i < s_timer_info.size(); ++i)
//		{
//			if(s_timer_info[i].event_id == event_id_tmp)
//				return;
//		}
//
//		s_timer_info.push_back(timer_info());
//		timer_info& info = s_timer_info.back();
//		info.pause = false;
//		info.pthis = this;
//		info.timer_fun = fun;
//		info.instance_count = m_instance_index;
//		info.event_id = event_id_tmp;
//		info.real_event_id = event_id;
//		info.timer_id = ::SetTimer(NULL, NULL, elapse, timer_proc);
//	}
//
//	void stop(int event_id)
//	{
//		std::vector<timer_info>::iterator ite = find(event_id);
//		if(ite != s_timer_info.end())
//		{
//			::KillTimer(NULL, (*ite).timer_id);
//			s_timer_info.erase(ite);
//		}
//	}
//
//	void pause(int event_id)
//	{
//		std::vector<timer_info>::iterator ite = find(event_id);
//		if(ite != s_timer_info.end())
//		{
//			(*ite).pause = true;
//		}
//	}
//
//	void resume(int event_id)
//	{
//		std::vector<timer_info>::iterator ite = find(event_id);
//		if(ite != s_timer_info.end())
//		{
//			(*ite).pause = false;
//		}
//	}
//
//private:
//	timer(const timer&);
//	void operator=(const timer&);
//
//private:
//	static void CALLBACK timer_proc(HWND /*hwnd*/, UINT /*uiMsg*/, UINT idEvent, DWORD /*dwTime*/)
//	{
//		UINT_PTR timer_id = idEvent;
//
//		timer_info* pTimerInfo = get_info(timer_id);
//		if(pTimerInfo == NULL)
//			return;
//
//		if(!pTimerInfo->pause)
//			pTimerInfo->timer_fun(pTimerInfo->real_event_id);
//	}
//
//	static timer_info* get_info(UINT_PTR timer_id)
//	{
//		for(size_t i = 0; i < s_timer_info.size(); ++i)
//		{
//			if(s_timer_info[i].timer_id == timer_id)
//				return &s_timer_info[i];
//		}
//
//		return NULL;
//	}
//
//	std::vector<timer_info>::iterator find(int event_id)
//	{
//		std::vector<timer_info>::iterator ite = s_timer_info.begin();
//		for(; ite != s_timer_info.end(); ++ite)
//		{
//			int tmp_event_id = ((*ite).instance_count << 24) + event_id;
//			if((*ite).event_id == tmp_event_id)
//			{
//				return ite;
//			}
//		}
//
//		return s_timer_info.end();
//	}
//};

} // end of namespace gtl